package org.jugile.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;


public class Mail {

	private String header;
	private String body;
	private Date received;
	private Date sent;
	
	private MailContact from;
	private MailContact replyto;
	private List<MailContact> to = new ArrayList<MailContact>();
	private List<MailContact> cc = new ArrayList<MailContact>();
	private List<MailContact> bcc = new ArrayList<MailContact>();

	private List<Blob> attachments = new ArrayList<Blob>();
	private boolean isHtml = false;
	
	
	public Mail() {
		
	}
	
	public static void main(String args[]) {

	}
	
	public void setHtml() { isHtml = true; }
	
	private enum Mode { TRASH, DELETE, NONE };
	
	public static List<Mail> fetchAndDelete() { return fetch(Mode.DELETE); }
	public static List<Mail> fetchAndTrash() { return fetch(Mode.TRASH); }
	public static List<Mail> fetch() { return fetch(Mode.NONE); }
	public static List<Mail> fetch(Mode mode) {
		try {
			// gmail
			String host = "imap.gmail.com";
			String user = Props.get("jugile.mod.gmail.user");
			String passwd = Props.get("jugile.mod.gmail.passwd");
	
			// Create empty properties
			Properties props = new Properties();
			// IMAP provider
			props.setProperty( "mail.imap.port", "993");
			props.setProperty( "mail.imap.socketFactory.port", "993");
			props.setProperty( "mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
			
			// Get session
			//Session session = Session.getDefaultInstance(props, null);
			Session session = Session.getInstance(props, null);
			
			// Get the store
			Store store = session.getStore("imap");
			store.connect(host, user, passwd);
	
			// Get folder
			Folder inbox = store.getFolder("Inbox");
			Folder trash = store.getFolder("[Gmail]/Trash");

			if (mode == Mode.NONE) inbox.open(Folder.READ_ONLY);
			else inbox.open(Folder.READ_WRITE);
			if (mode == Mode.TRASH) trash.open(Folder.READ_WRITE);
			
			// Get directory
			Message msgs[] = inbox.getMessages();
		
			List<Mail> res = new ArrayList<Mail>();
			for (int i=0, n=msgs.length; i<n; i++) {
				Mail m = new Mail();
				Message msg = msgs[i];
				m.setFrom(msg.getFrom()[0].toString());
				m.setReceived(msg.getReceivedDate());
				m.setHeader(msg.getSubject());
				m.setReplyto(msg.getReplyTo()[0].toString());
				
				Object content = msg.getContent();
				if (content instanceof Multipart) {
					handleMultipart(m, (Multipart)content);
				} else {
					handlePart(m, msg);
				}
				
				res.add(m);
				if (mode == Mode.DELETE) 
					msg.setFlag(Flags.Flag.DELETED, true);
			}
			if (mode == Mode.TRASH) inbox.copyMessages(msgs, trash);

			// Close connection
			if (mode == Mode.NONE) inbox.close(false);
			else inbox.close(true);
			if (mode == Mode.TRASH) trash.close(true);
			store.close();
			return res;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

	}
	
	public void send() {
		try {
			send(this);
			sent = new Date();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	public static void send(Mail msg) throws Exception {
		String host = Props.get("jugile.mail.server");
		String user = Props.get("jugile.mail.user");
		String passwd = Props.get("jugile.mail.passwd");
		String from = Props.get("jugile.mail.from");

		// Get system properties
		Properties props = System.getProperties();

		// Setup mail server
		props.put("mail.smtp.host", host);
		props.put("mail.smtp.port", "587");
		props.put("mail.smtp.user", user);
		props.put("mail.smtp.starttls.enable","true");
		props.put("mail.smtp.auth", "true");  // If you need to authenticate
		
		// Use the following if you need SSL
		//props.put("mail.smtp.socketFactory.port", "587");
		//props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
		//props.put("mail.smtp.socketFactory.fallback", "false");
		// Get session
		Session session = Session.getDefaultInstance(props, null);

		// Define message
		MimeMessage message = new MimeMessage(session);
		message.setFrom(new InternetAddress(from));
		for (MailContact c : msg.getTo()) {
			message.addRecipient(Message.RecipientType.TO, new InternetAddress(c.getEmail()));			
		}
		for (MailContact c : msg.getCc()) {
			message.addRecipient(Message.RecipientType.CC, new InternetAddress(c.getEmail()));			
		}
		for (MailContact c : msg.getBcc()) {
			message.addRecipient(Message.RecipientType.BCC,	new InternetAddress(c.getEmail()));			
		}
		message.setSubject(msg.getHeader());

		String type = "text/plain";
		if (msg.isHtml) type = "text/html; charset=utf-8";
		
		if (msg.getAttachments().size() == 0) {
			message.setContent(msg.getBody(),type);
		} else {
		    // create the message part 
		    MimeBodyPart messageBodyPart = new MimeBodyPart();
		    //fill message
		    messageBodyPart.setContent(msg.getBody(),type);
		    Multipart multipart = new MimeMultipart();
		    multipart.addBodyPart(messageBodyPart);

		    for (Blob b : msg.getAttachments()) {
			    messageBodyPart = new MimeBodyPart();
			    DataSource source = new ByteArrayDataSource(b.getData(), b.getType());
			    messageBodyPart.setDataHandler(new DataHandler(source));
			    messageBodyPart.setFileName(b.getFilename());
			    if (b.getCid() != null) messageBodyPart.setHeader("Content-ID","<"+b.getCid()+">");
			    multipart.addBodyPart(messageBodyPart);
		    }

		    // Put parts in message
		    message.setContent(multipart);	
		}

		// Send message
		Transport transport = session.getTransport("smtp");
		transport.connect(host, user, passwd);
		transport.sendMessage(message, message.getAllRecipients());
		transport.close();
		//session.notify();

	}
	
	
	private static void handleMultipart(Mail m, Multipart multipart) 
	throws MessagingException, IOException {
		for (int i=0, n=multipart.getCount(); i<n; i++) {
			handlePart(m, multipart.getBodyPart(i));
		}
	}
	
	private static void handlePart(Mail m, Part part) throws MessagingException, IOException {
		String disposition = part.getDisposition();
		String contentType = part.getContentType();
		if (disposition == null) { // When just body
			print("Null: "  + contentType);
			// Check if plain
			if ((contentType.length() >= 10) && 
				(contentType.toLowerCase().substring(0, 10).equals("text/plain"))) {
				Blob b = new Blob(part.getInputStream());
				m.setBody(b.toString());
			} else { // Don't think this will happen
				print("Other body: " + contentType);
				m.add(part.toString());
			}
		} else if (disposition.equalsIgnoreCase(Part.ATTACHMENT)) {
			print("Attachment: " + part.getFileName() +	" : " + contentType);
			Blob b = new Blob(part.getInputStream());
			b.setFilename(part.getFileName());
			b.setType(contentType);
			m.addAttachment(b);
		} else if (disposition.equalsIgnoreCase(Part.INLINE)) {
			print("Inline: " + part.getFileName() +	" : " + contentType);
			Blob b = new Blob(part.getInputStream()); 
			b.setFilename(part.getFileName());
			b.setType(contentType);
			m.addAttachment(b);
		} else {  // Should never happen
			print("Other: " + disposition);
		}
	}
	
	
	
	public String getHeader() {
		return header;
	}

	public void setHeader(String header) {
		this.header = header;
	}

	public void add(String str) {
		if (body == null) body = "";
		body += str + "\n";
	}
	public String getBody() {
		return body;
	}
	
	public void setBody(String body) {
		this.body = body;
	}

	public MailContact getFrom() {
		return from;
	}

	public void setFrom(MailContact from) {
		this.from = from;
	}
	public void setFrom(String v) { from = new MailContact(v); }
	
	public MailContact getReplyto() {
		return replyto;
	}

	public void setReplyto(MailContact replyto) {
		this.replyto = replyto;
	}
	public void setReplyto(String v) { replyto = new MailContact(v); }

	public List<MailContact> getTo() {
		return to;
	}
	public void addTo(MailContact c) { to.add(c);}
	public void addTo(String email) { addTo(new MailContact(email));}

	public List<MailContact> getCc() {
		return cc;
	}
	public void addCc(MailContact c) { cc.add(c);}
	public void addCc(String email) { addCc(new MailContact(email));}

	public List<MailContact> getBcc() {
		return bcc;
	}
	public void addBcc(MailContact c) { bcc.add(c);}
	public void addBcc(String email) { addBcc(new MailContact(email));}

	private void setReceived(Date d) { received = d; }
	private void setReceived() { received = new Date(); }
	
	
	public List<Blob> getAttachments() { return attachments; }
	public void addAttachment(Blob b) { attachments.add(b); }
	
		
	
	public static void print(String msg) { System.out.println(msg); }

	
	
}
